/*=+--+=#=+--     Unmanned Aerial System Management Software      --+=#=+--+=#*\
|          Copyright (C) 2011 Regents of the University of Colorado.           |
|                             All Rights Reserved.                             |

     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License version 2 as
     published by the Free Software Foundation.

     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.

            Jack Elston                       Cory Dixon                        
|           elstonj@colorado.edu              dixonc@colorado.edu              |
|                                                                              |
\*=+--+=#=+--                 --+=#=+--+=#=+--                    --+=#=+--+=#*/

#include <iostream>
#include <stdlib.h>
#include <stdio.h>

#include "windManager.h"
#include "vcConfig.h"

using namespace std;

/*<---Global Variables---->*/
extern GuiStruct guiStr;


WindManager::WindManager()
{
	minRange   = 20; // in units of window pixels
	lastAdd    = -1;
	buffLength = 30; //MAX_WIND_MEASUREMENTS;
	scale_item = NULL;
	scale_item_h = NULL;
	scale_widget = NULL;
}

WindManager::~WindManager()
{
}

void WindManager::init(GPSMap *map)
{
	LayerManager::init(map);
	layerG = (GooCanvasItem*)gps_map->getLayerG(GPSMap::WIND_LAYER);
}

void WindManager::scale_value_cb(GtkRange* range, gpointer func_data)
{
	WindManager *me = (WindManager *)func_data;
	if( !me ) return;

	double val = gtk_range_get_value(range);
	//cout << "WindManager::scale_value_cb - " << val << endl;
	//gtk_widget_queue_draw(GTK_WIDGET(range));
	//gtk_range_set_value(range,val);

	me->buffLength = (int)val;
}


void WindManager::updateMap()
{
	/*WindData_t wind;
	  wind.south = 2;
	  wind.west = 1;
	  wind.gps.pos.lat = 40.0;
	  wind.gps.pos.lon = -105.0;
	  add_windData(0,&wind);*/
}

bool WindManager::received_data(int clientInd, uint8_t * buf) 
{
	WindData_t * wind = (WindData_t *)CMD_DATAPTR(buf);

	add_windData(clientInd,wind);
	return true;
}

bool WindManager::add_windData(int clientInd, WindData_t * wind)
{
	//-- do one time init for goocanvas group and map layer list
	if( !scale_item_h ) {
		// goocanvas
		scale_item_h = GOO_CANVAS_ITEM(goo_canvas_group_new (gps_map->getRootItem(), NULL));

		// add the scale item to the map layer list
		gps_map->addLayer( "Time Range", scale_item_h, (GooCanvasItem *)GPSMap::WIND_LAYER );
		gps_map->setLayerListShown(scale_item_h, true );
	}

	//-- Create group to hold wind field for a client
	if( windValues[clientInd].wind_group == NULL ) 
	{
		windValues[clientInd].wind_group = goo_canvas_group_new (GOO_CANVAS_ITEM(layerG), NULL);

		String nameStr;
		if( clientInd == MAX_CLIENTS )
			nameStr = "Local";
		else {
			Client *a_client = guiStr.shm_ptr->clients.getClient(clientInd);
			if( a_client )
				nameStr = a_client->name;
			else {
				char name[32];
				sprintf(name, "Client-%i",clientInd);
				nameStr = name;
			}
		}
		// add and show layer in layer list
		gps_map->addLayer( nameStr, windValues[clientInd].wind_group, 
				(GooCanvasItem*)GPSMap::WIND_LAYER);
		gps_map->setLayerListShown( windValues[clientInd].wind_group, 1);

		// make sure parent manager layer is shown
		gps_map->toggleLayerShown(GPSMap::WIND_LAYER, true);
	}

	// now increment buffer and clear
	int index = (windValues[clientInd].last+1) % MAX_WIND_MEASUREMENTS;
	windValues[clientInd].last = index;
	if(windValues[clientInd].first == windValues[clientInd].last) {
		windValues[clientInd].first = ++windValues[clientInd].first % MAX_WIND_MEASUREMENTS;
	}

	if(windValues[clientInd].icons[index].item != NULL) {
		if( GOO_IS_CANVAS_ITEM(windValues[clientInd].icons[index].item ) )
			goo_canvas_item_remove(windValues[clientInd].icons[index].item);
		windValues[clientInd].icons[index].item = NULL;
	}

	// wind field values
	windValues[clientInd].data[index] = *wind;

	// create new flag
	windValues[clientInd].icons[index].item = GOO_CANVAS_ITEM(goo_canvas_group_new(
				windValues[clientInd].wind_group,
				"x", 0.0,
				"y", 0.0,
				NULL));

	double x = wind->gps.pos.lon;
	double y = wind->gps.pos.lat;
	double yaw = atan2(wind->south, -wind->west);
	double mag = sqrt((wind->south)*(wind->south)+(wind->west)*(wind->west)) * 1.94384449; //kts

	create_wind_icon(clientInd, index, mag);
	gps_map->geoToLayer(GPSMap::WIND_LAYER, x, y);
	goo_canvas_item_set_simple_transform( windValues[clientInd].icons[index].item, 
			x, y, 1.0, yaw*180/M_PI);

	windValues[clientInd].icons[index].img_x = x;
	windValues[clientInd].icons[index].img_y = y;

	if( lastAdd >= 0 ) {
		double x1 = windValues[clientInd].icons[lastAdd].img_x;
		double y1 = windValues[clientInd].icons[lastAdd].img_y;

		// see if we need to hide the flag because of scaling
		double range = sqrt( (x-x1)*(x-x1) + (y-y1)*(y-y1) );
		if(range < minRange) {
			g_object_set (windValues[clientInd].icons[index].item, "visibility", 
					GOO_CANVAS_ITEM_INVISIBLE, NULL);
		} else
			lastAdd = index;
	} else
		lastAdd = index;

	// now loop through all and hide any old ones that should not be displayed
	index = windValues[clientInd].last;
	int stop  = windValues[clientInd].first;
	int count = 0;

	while( index != stop ) {
		if( windValues[clientInd].icons[index].item ) {
			count++;
			if( count > buffLength )
				g_object_set (windValues[clientInd].icons[index].item, "visibility", 
						GOO_CANVAS_ITEM_INVISIBLE, NULL);
		}
		index--;
		if( index < 0 ) index = MAX_WIND_MEASUREMENTS - 1;
	}

	return true;
}


void WindManager::create_wind_icon(int clientInd, unsigned int ind, float speed_f)
{
	windValues[clientInd].icons[ind].station = goo_canvas_ellipse_new( windValues[clientInd].icons[ind].item,
			0.0, 0.0,
			2.0, 2.0,
			"stroke-color", windValues[clientInd].icons[ind].color_str,
			"line-width", 2.0,
			NULL);

	int speed = (int)speed_f;

	if(speed < 5) {
		windValues[clientInd].icons[ind].calm = goo_canvas_ellipse_new( windValues[clientInd].icons[ind].item,
				0.0, 0.0,
				10.0, 10.0,
				"stroke-color", windValues[clientInd].icons[ind].color_str,
				"line-width", 2.0,
				NULL);
	}
	else {
		windValues[clientInd].icons[ind].stem = goo_canvas_polyline_new (windValues[clientInd].icons[ind].item, FALSE, 2,
				2.0, 0.0,
				32.0, 0.0,
				"stroke-color", windValues[clientInd].icons[ind].color_str,
				"line-width", 2.0,
				"end-arrow", FALSE,
				NULL);

		int barb_num = 0;

		int tens = (int)floor((speed % 50) / 10);
		for(int i = 0; i < tens; i++) {
			windValues[clientInd].icons[ind].barbs[barb_num] = goo_canvas_polyline_new (windValues[clientInd].icons[ind].item, FALSE, 2,
					32.0-6.0*(double)barb_num, 0.0,
					36.0-6.0*(double)barb_num, 20.0,
					"stroke-color", windValues[clientInd].icons[ind].color_str,
					"line-width", 2.0,
					"end-arrow", FALSE,
					NULL);
			barb_num ++;
		}

		if(!barb_num) barb_num++;

		if((int)floor(speed/5)%2 == 1) {
			windValues[clientInd].icons[ind].barbs[barb_num] = goo_canvas_polyline_new (windValues[clientInd].icons[ind].item, FALSE, 2,
					32.0-6.0*(double)barb_num, 0.0,
					34.0-6.0*(double)barb_num, 10.0,
					"stroke-color", windValues[clientInd].icons[ind].color_str,
					"line-width", 2.0,
					"end-arrow", FALSE,
					NULL);
			barb_num ++;
		}

		barb_num = 0;
		int fiftys = (int)floor(speed / 50);
		if (fiftys > MAX_BARBS - 5) fiftys = MAX_BARBS - 5; //maybe on jupiter this might max out
		for(int i = 0; i < fiftys; i++) {
			windValues[clientInd].icons[ind].barbs[barb_num] = goo_canvas_polyline_new (windValues[clientInd].icons[ind].item, FALSE, 4,
					34.0+14.0*(double)barb_num, 0.0,
					46.0+14.0*(double)barb_num, 12.0,
					46.0+14.0*(double)barb_num, 0.0,
					32.0+14.0*(double)barb_num, 0.0,
					"stroke-color", windValues[clientInd].icons[ind].color_str,
					"line-width", 2.0,
					"end-arrow", FALSE,
					NULL);

			g_object_set(G_OBJECT(windValues[clientInd].icons[ind].barbs[barb_num]), "fill-color", windValues[clientInd].icons[ind].color_str, NULL);

			barb_num ++;
		}
	}
}

void WindManager::translate_from_zoomChange(double zoom_factor)
{
	int i = 0;
	double x, y, scale, rot;

	//cout << " WindManager::translate_from_zoomChange " << endl;

	for( i=0; i < MAX_CLIENTS; i++)
	{
		if( windValues[i].wind_group ) 
		{
			int stop  = windValues[i].first;
			int index = windValues[i].last;
			int useI  = index;
			double xl = 0, yl = 0;
			int count = 0;

			while( index != stop ) {
				if( windValues[i].icons[index].item ) {
					count++;
					GooCanvasItem* icon = windValues[i].icons[index].item;

					// stop any animation to get real position
					goo_canvas_item_stop_animation(icon);

					double x1 = windValues[i].data[index].gps.pos.lon;
					double y1 = windValues[i].data[index].gps.pos.lat;

					gps_map->geoToLayer(GPSMap::WIND_LAYER, x1, y1);

					windValues[i].icons[index].img_x = x1;
					windValues[i].icons[index].img_y = y1;

					double range = sqrt( (xl-x1)*(xl-x1) + (yl-y1)*(yl-y1) );

					if( (useI == index || range >= minRange) && count <= buffLength ) {
						goo_canvas_item_get_simple_transform(icon, &x, &y, &scale, &rot);
						goo_canvas_item_set_simple_transform( icon, x1, y1, scale, rot);
						g_object_set (windValues[i].icons[index].item, "visibility", 
								GOO_CANVAS_ITEM_VISIBLE, NULL);

						useI = index;
						xl = x1;
						yl = y1;
					} else
						g_object_set (windValues[i].icons[index].item, "visibility", 
								GOO_CANVAS_ITEM_INVISIBLE, NULL);

				}
				index--;
				if( index < 0 ) index = MAX_WIND_MEASUREMENTS - 1;
			}
		}
	}
}

void WindManager::create_scale()
{
	if( scale_item )
		goo_canvas_item_remove(scale_item);
	scale_item = GOO_CANVAS_ITEM(goo_canvas_group_new (scale_item_h, NULL));

	// scale widget
	scale_widget = GTK_SCALE(gtk_hscale_new_with_range(0, MAX_WIND_MEASUREMENTS, 1));
	gtk_scale_set_draw_value(scale_widget,1);
	gtk_scale_set_digits(scale_widget, 0);
	gtk_scale_set_value_pos(scale_widget, GTK_POS_BOTTOM);
	gtk_range_set_value(GTK_RANGE(scale_widget), buffLength);

	// signal for value change
	gtk_signal_connect(GTK_OBJECT(scale_widget), "value_changed", 
			GTK_SIGNAL_FUNC(scale_value_cb), this);

	scale_item = goo_canvas_widget_new (scale_item_h, GTK_WIDGET(scale_widget),
			240.0, 13.0, 100.0, -1.0,
			"anchor", GTK_ANCHOR_NW,
			NULL);
}

bool WindManager::setShown(int setTo)
{
	//cout << "WindManager::setShown setTo=" << setTo << endl;
	LayerManager::setShown(setTo);

	if(!setTo) {
		if( scale_item ) 
			goo_canvas_item_remove(scale_item);
		scale_item = NULL;
	} else {
		create_scale();
	}

	return true;
}
